#ifndef INI_INCLUDE_H
#define INI_INCLUDE_H

#include <string>
#include <map>
#include <vector>
#include <fstream>
#include <string.h>

using std::string;
using std::map;
using std::vector;
using std::fstream;
using std::pair;

static string vecBegin("[[");
static string vecEnd("]]");

class CIni
{
public:
    // arraySep only used in this case: val=[[1 2 3]]
    // not used int mutil-line array value
    CIni(unsigned int lineMaxLen = 1024,const string& arraySep = " ")
        :m_lineMaxLen(lineMaxLen)
    {}

    CIni(const string& path, const string& arraySep = " ", unsigned int lineMaxLen = 1024)
        :m_lineMaxLen(lineMaxLen)
    {
        parse(path,arraySep);
    }

    // 获取配置值
    string getString(const string& section,const string& option,const string& defaultVal = "");
    bool getArray(const string& section,const string& option,vector<string>& array);
    // 把另一个配置文件中的配置，添加到本配置
    // 遇到重复的，使用最新的值
    // arraysep only used in this case: val=[[1 2 3]]
    bool append(const string& path,const string& arraySep = " ", bool sec_overwrite = false,
            unsigned int lineMaxLen = 1024);

    void enumAllSection(map<string,map<string,string> >& setting)
    {
        setting = m_settingSec;
    }
    // 获取所有的节点名
    int enumAllSection(vector<string> &setting);
    // 获取某个段的所有配置
    void enumSection(const string& section,map<string,string>& setting);
    // 获取某个section下以前缀字符串开头的一些配置，不支持通配符
    // 前缀字符串必须是完整的某个层级,比如 logging.level.
    void enumSection(const string& section,const string& prefix,map<string,string>& setting);

    // 某个section 是否存在
    bool secExist(const string& section);

    void clear()
    {
        m_settingSec.clear();
        m_vsettingSec.clear();
        m_sec_comment.clear();
        m_opt_comment.clear();
    }

    // 更新一个option的值，包括更新值为 array 的
    void setField(const string& section,const string& option,const string& val);

    // 保存到一个文件
    bool saveTo(const string& filepath);

    // 获取内部保存的数组值分隔符
    string getArraySep()
    {
        return vecEnd;
    }
private:
    int stringSplit(const string& str, const string& sep, vector<string> &vec );
    string& trim(string& str, const string& trimStr = " \r\n\t");
    // 注释以;或#或//开头
    bool parse(const string& path,const string& arrSep = " ", bool sec_overwrite = false);

    // 保存一个段
    void writeSec(fstream& f, const string& section, vector<pair<string,string> >& sec_opt);

    // 更新某个段的配置
    void updateSec(const string& section, map<string,string>& secSetting,bool sec_overwrite);
    void updateVSec(vector<pair<string,string> >& dest,
            const vector<pair<string,string> >& src,bool overwrite);

    bool isVvSecExist(const string& sec);

    unsigned int        m_lineMaxLen;
    // 按section存储
    map<string,map<string,string> > m_settingSec;
    map<string,vector< pair<string,string> > > m_vsettingSec;

    // 注释
    // sec:comment
    map<string,string>              m_sec_comment;
    // sec:<opt,comment>
    map<string,map<string,string> >  m_opt_comment;
};

inline int CIni::stringSplit(const string& str, const string& sep, vector<string> &vec )
{
    if (str.empty() || sep.empty())
        return 0;

    size_t posBegin = 0;
    size_t posEnd = str.find(sep,posBegin);
    while(posEnd != string::npos)
    {
        vec.push_back(str.substr(posBegin,posEnd-posBegin));
        posBegin = posEnd + sep.length();
        posEnd = str.find(sep,posBegin);
    }
    if (posBegin < str.length() )
        vec.push_back(str.substr(posBegin));
    else if (posBegin >= str.length() )
        vec.push_back("");

    return vec.size();
}

inline string CIni::getString(const string& section,const string& option,const string& defaultVal)
{
    map<string, map<string,string> >::iterator it;
    it = m_settingSec.find(section);
    if ( it == m_settingSec.end() )
        return defaultVal;

    map<string,string>::iterator itopt = it->second.find(option);
    if (itopt != it->second.end())
        return itopt->second;

    return defaultVal;
}

inline bool CIni::getArray(const string& section,const string& option,vector<string>& array)
{
    map<string, map<string,string> >::iterator it;
    it = m_settingSec.find(section);
    if ( it == m_settingSec.end() )
        return false;

    map<string,string>::iterator itopt = it->second.find(option);
    if (itopt != it->second.end())
    {
        if ( itopt->second.find(vecEnd) != string::npos )
        {
            stringSplit(itopt->second,vecEnd,array);
            return true;
        }
    }

    return false;
}

inline int CIni::enumAllSection(vector<string> &setting)
{
    int cnt = 0;
    setting.clear();
    for(map<string, map<string, string> >::iterator it = m_settingSec.begin();
        it != m_settingSec.end(); ++it)
    {
        setting.push_back(it->first);
        ++cnt;
    }
    return cnt;
}

inline void CIni::enumSection(const string& section,map<string,string>& setting)
{
    map<string,map<string,string> >::iterator itSec;
    itSec = m_settingSec.find(section);
    if (itSec == m_settingSec.end())
        return;
    setting = itSec->second;
}

inline void CIni::enumSection(const string& section,const string& prefix,
        map<string,string>& setting)
{
    string tmppref(prefix);
    if (!tmppref.empty() && tmppref[tmppref.length() - 1] != '.')
        tmppref = tmppref + ".";

    map<string,string>::iterator it;
    map<string,string> sectionSetting;
    enumSection(section,sectionSetting);

    for(it = sectionSetting.begin(); it != sectionSetting.end(); ++it)
    {
        if ( it->first.find(tmppref) == 0)
            setting[it->first] = it->second;
    }
}

inline bool CIni::append(const string& path,const string& arraySep,bool sec_overwrite,
        unsigned int lineMaxLen)
{
    if (m_lineMaxLen < lineMaxLen)
        m_lineMaxLen = lineMaxLen;

    return parse(path,arraySep,sec_overwrite);
}

inline string& CIni::trim(string& str, const string& trimStr /* = " \r\n\t" */)
{
    size_t len = str.length();
    size_t begin = 0,end=len;
    
    while(begin < len)
    {
        if (trimStr.find(str[begin]) == string::npos)
            break;
        ++begin;
    }
    while(end - 1 > 0 && end - 1 > begin)
    {
        if (trimStr.find(str[end - 1]) == string::npos)
            break;
        --end;
    }
    
    str = str.substr(begin,end - begin);
    return str;
}

inline bool CIni::parse(const string& path,const string& arrSep, bool sec_overwrite)
{
    // 注释以 #或;开始，不支持行尾及行中注释
    if (path.empty()) return false;

    fstream f(path.c_str());
    if (!f.good()) return false;
    
    char* buf = new char[m_lineMaxLen];
    string section;
    map<string,string> sectionSetting;
    map<string,map<string,string> >::iterator itcomment;

    vector<pair<string,string> > vSec;
    vector<pair<string,string> >::iterator itv;

    string  comment;
    bool isVec = false;
    string opt,val;
    while(!f.eof())
    {
        memset(buf,0,m_lineMaxLen);
        f.getline(buf,m_lineMaxLen);
        
        string str(buf);
        trim(str);

        if ( str.empty())
            continue;

        // 去掉单行注释，忽略 array 值的 注释
        if ( str[0] == '#' || str[0] == ';')
        {
            if ( isVec )
                continue;
            comment += str + "\n";
            continue;
        }

        if (str.size() > 1 && (str[0] == '/' && str[1] == '/') )
        {
            if (isVec )
                continue;
            comment += str + "\n";
            continue;
        }
        if ( str == vecBegin )
        {
            isVec = true;
            continue;
        }
        if ( str == vecEnd )
        {
            isVec = false;
            string tmpopt = trim(opt);
            if ( !comment.empty())
            {
                m_opt_comment[section][tmpopt] = comment;
            }

            // empty array
            if (val.empty())
                val += vecEnd;

            sectionSetting[tmpopt] = val;
            vSec.push_back(pair<string,string>(tmpopt,val));
            comment.clear();
            continue;
        }
        // 保存段名
        if (str[0] == '[')
        {
            size_t pos = str.find("]");
            if (pos != string::npos)
            {
                map< string, vector< pair<string,string> > >::iterator itvs;
                itvs = m_vsettingSec.find(section);
                if ( itvs == m_vsettingSec.end() )
                {
                    m_vsettingSec[section] = vSec;
                }
                else
                {
                    updateVSec(itvs->second,vSec,sec_overwrite);
                }

                updateSec(section,sectionSetting,sec_overwrite);

                sectionSetting.clear();
                vSec.clear();

                str = str.substr(1,pos - 1);
                section = trim(str);
                if ( !comment.empty() )
                    m_sec_comment[section] = comment;
                comment.clear();
            }
            continue;
        }
        
        // 解析选项及值
        size_t delimPos;
        delimPos = str.find("=");
        if (delimPos == string::npos)
        {
            if ( isVec )
            {
                if ( val.empty() )
                    val = str;
                else
                    val += vecEnd + str;
            }
            continue;
        }

        opt = str.substr(0,delimPos);
        val = str.substr(delimPos + 1);

        if (delimPos == str.size() )
        {
            string tmpopt = trim(opt);
            if ( !comment.empty())
            {
                m_opt_comment[section][tmpopt] = comment;
            }
            sectionSetting[tmpopt] = "";
            vSec.push_back(pair<string,string>(tmpopt,""));
        }
        else
        {
            val = trim(val);
            size_t vecpos = val.find(vecBegin);
            if ( vecpos != string::npos )
            {
                if ( val.length() > vecpos + vecBegin.length())
                {
                    val = val.substr(vecpos + vecBegin.length());
                }
                else
                    val.clear();

                val = trim(val);
                if ( !val.empty() )
                {
                    vecpos = val.find(vecEnd);
                    if ( vecpos != string::npos )
                    {
                        val.substr(0,vecpos);
                        val = trim(val);
                        vector<string> vval;
                        stringSplit(val,arrSep,vval);
                        vector<string>::iterator itvv;
                        string tmpval;
                        for( itvv = vval.begin(); itvv != vval.end(); ++itvv )
                        {
                            if (tmpval.empty())
                                tmpval = *itvv;
                            else
                                tmpval += vecEnd + *itvv;
                        }
                        val = tmpval;
                        goto VecEnd;
                    }
                }
                
                val = trim(val);
                isVec = true;
                continue;
            }
            VecEnd:
            string tmpopt = trim(opt);
            if ( !comment.empty())
            {
                m_opt_comment[section][tmpopt] = comment;
            }
            sectionSetting[tmpopt] = val;
            vSec.push_back(pair<string,string>(tmpopt,val));
        }
        comment.clear();
    }
    // 最后一个section
    map< string, vector< pair<string,string> > >::iterator itvs;
    itvs = m_vsettingSec.find(section);
    if ( itvs == m_vsettingSec.end() )
    {
        m_vsettingSec[section] = vSec;
    }
    else
    {
        updateVSec(itvs->second,vSec,sec_overwrite);
    }

    updateSec(section,sectionSetting,sec_overwrite);

    delete []buf;
    f.close();

    return true;
}

inline void CIni::updateSec(const string& section,map<string,string>& secSetting,bool sec_overwrite)
{
    map<string,map<string,string> >::iterator it;
    // 把后面的插入原有的值
    map<string,string>::iterator itopt,itsec;
    for(itsec = secSetting.begin(); itsec != secSetting.end(); ++itsec)
    {
        it = m_settingSec.find(section);
        if ( it == m_settingSec.end() )
        {
            m_settingSec[section] = secSetting;
            break;
        }

        map<string,string>& sec_val = it->second;

        itopt = sec_val.find(itsec->first);
        if ( itopt == sec_val.end() )
        {
            sec_val[itsec->first] = itsec->second;
            continue;
        }

        if ( sec_overwrite )
        {
            if ( itopt->second.find(vecEnd) != string::npos )
                itopt->second += vecEnd  + itsec->second;
            else
                sec_val[itsec->first] = itsec->second;
            continue;
        }
    }
}

inline void CIni::updateVSec(vector<pair<string,string> >& dest,
            const vector<pair<string,string> >& src,bool overwrite)
{
    vector<pair<string,string> >::iterator itd;
    vector<pair<string,string> >::const_iterator its;
    for( its = src.begin(); its != src.end(); ++its)
    {
        bool bfind = false;
        for(itd = dest.begin(); itd != dest.end(); ++itd)
        {
            if ( itd->first != its->first )
                continue;
            bfind = true;
            break;
        }
        if ( !bfind )
        {
            dest.push_back(*its);
            continue;
        }
        else
        {
            if ( overwrite )
            {
                // array val
                if ( itd->second.find(vecEnd) != string::npos )
                    itd->second += vecEnd + its->second;
                else
                    itd->second = its->second;
            }
        }
    }
}

inline void CIni::setField(const string& section,const string& option,const string& val)
{
    vector<pair<string,string> > vtmp;
    vtmp.push_back(pair<string,string>(option,val));

    // 更新vsettingSec
    map<string,vector<pair<string,string> > >::iterator itvm;
    itvm = m_vsettingSec.find(section);
    
    if ( itvm == m_vsettingSec.end() )
    {
        m_vsettingSec[section] = vtmp;
    }
    else
    {
        updateVSec(itvm->second,vtmp,true);
    }
    
    // 更新settingSec
    map<string,string> maptmp;
    maptmp[option]  = val;

    map<string,map<string,string> >::iterator itm;
    itm = m_settingSec.find(section);
    if ( itm == m_settingSec.end() )
    {
        m_settingSec[section] = maptmp;
    }
    else
    {
        updateSec(section,maptmp,true);
    }
}

inline bool CIni::secExist(const string& section)
{
    return !(m_settingSec.find( section ) == m_settingSec.end());
}

inline void CIni::writeSec(fstream& f, const string& section, vector<pair<string,string> >& sec_opt)
{
    vector< pair<string,string> >::iterator itopt;
    map<string,string>::iterator itcomment = m_sec_comment.find(section);
    // write section comment
    // comment has \n
    if ( itcomment != m_sec_comment.end() && !itcomment->second.empty())
        f << itcomment->second;
    if ( !section.empty() )
        f<<"["<<section<<"]"<<"\n";

    // write opt comment
    map<string,string>& opt_comment = m_opt_comment[section];
    for( itopt = sec_opt.begin(); itopt != sec_opt.end(); ++itopt )
    {
        itcomment = opt_comment.find(itopt->first);
        if ( itcomment != opt_comment.end() )
        {
            f<<itcomment->second;
        }
        // array
        f<<itopt->first<<"\t\t= ";
        if ( itopt->second.find( vecEnd ) != string::npos )
        {
            f<<vecBegin<<"\n";
            vector<string> vval;
            vector<string>::iterator it;
            stringSplit(itopt->second,vecEnd,vval);
            for(it = vval.begin(); it != vval.end(); ++it)
            {
                f<<*it<<"\n";
            }
            f<<vecEnd<<"\n";
        }
        else
            f<<itopt->second<<"\n";
    }
    f<<"\n";
}

inline bool CIni::saveTo(const string& filepath)
{
    if ( filepath.empty())
        return false;

    fstream f;
    f.open(filepath.c_str(),fstream::out|fstream::trunc);
    if ( !f.good())
        return false;

    string emptysec;
    //write empty section first
    map<string,vector<pair<string,string> > >::iterator it;
    it = m_vsettingSec.find(emptysec);
    if ( it != m_vsettingSec.end() )
    {
        writeSec(f,emptysec,it->second);
    }

    for( it = m_vsettingSec.begin(); it != m_vsettingSec.end(); ++it)
    {
        if ( emptysec == it->first )
            continue;
        writeSec(f,it->first,it->second);
    }

    f.close();
    return true;
}

#endif







